AddCSLuaFile("shared.lua")
AddCSLuaFile("cl_init.lua")

include("shared.lua")

function ENT:Initialize()
	self.Entity:PhysicsInit(SOLID_VPHYSICS)
	self.Entity:SetMoveType( MOVETYPE_VPHYSICS )
	self.Entity:SetTrigger( true )
	self.Entity:SetSolid(SOLID_VPHYSICS)
	self.Entity:SetUseType(SIMPLE_USE)

    self.steamcap = 0
	local phys = self.Entity:GetPhysicsObject()
	if phys:IsValid() then 
	phys:Wake() 
    phys:SetMass( math.ceil( phys:GetVolume()/6000)*10 )
    self.steamcap = math.ceil(phys:GetVolume()/100000)*100
	end
    
    self.steam = 0
    self.pressure = 1
    self.buildup = math.max( self.steamcap/(20+self.steamcap/400) , 15 )
    
    self.myhp = 100+math.ceil(math.pow(self.steamcap,0.8)/5)*5
    self.maxhp = self.myhp
    self.hp = self.maxhp
    self.seat = NULL
    self.driver = NULL
    self.lastdriver = NULL
    self.driverteam = -1
    self.aimPos = Vector(0,0,0)
    self.Core = self.Entity
    
    self.cost = math.ceil(self.buildup*10)
    self.Repay = 1
    Ent_InitCost( self.Entity )
    
    self.checktimer = CurTime()
    self.steamtimer = CurTime()
    self.Ents = {}
    self.Mods = {}
    self.dead = false
    
end

function WeldedTo( ent1, ent2 )

local constraints = constraint.FindConstraints( ent1, "Weld")

for _, con in pairs( constraints ) do
	if( con.Entity[1].Entity==ent1 and con.Entity[2].Entity==ent2 ) then return true end
	if( con.Entity[2].Entity==ent1 and con.Entity[1].Entity==ent2 ) then return true end
end

return false
end

function ENT:DoDeath( killer )
    if(self.dead==false) then
        self.Ents = constraint.GetAllConstrainedEntities( self.Entity )
        local salvage = 0.5
        
        for n, ent in pairs( self.Ents ) do
            if(ent:IsValid()) then
                if(table.HasValue(candamage,ent:GetClass())) then
                    local Vec = (ent:GetPos()-self.Entity:GetPos()):GetNormalized()
                    local gib = ents.Create( "st_propgib" )
                    gib:SetPos( ent:GetPos() )
                    gib:SetAngles( ent:GetAngles() )
                    gib:SetModel( ent:GetModel() )
                    gib:SetMaterial( ent:GetMaterial() )
                    gib:SetColor( ent:GetColor() )
                    gib:Activate()
                    gib:Spawn()
                    local phys = gib:GetPhysicsObject()
                    if(phys:IsValid()) then
                        phys:ApplyForceCenter((Vec+VectorRand():GetNormalized()*0.3)*2000000)
                    end
                end
                ent.Repay = salvage
                ent:Remove()
            end
        end
        
        self.dead = true
        self.Repay = salvage
        self.Entity:Remove()
    end
end

function ENT:Think()
    
    if( self.checktimer < CurTime() ) then
    
        //Some temporary variables that might be turned on or off based on the status of the ents we find
        local multicore_error = false
        local unweldedmods_error = false
        
        //Store the HP ratio now, then restore the same ratio after adjusting the maximum health.
        local hpratio = self.hp/self.maxhp  
        self.maxhp = self.myhp
        table.Empty( self.Mods )
        
        self.Ents = constraint.GetAllConstrainedEntities( self.Entity )
        
            for n, ent in pairs( self.Ents ) do
            local checkweld = false
                if( ent:IsValid() and ent:GetPhysicsObject():IsValid() and ent!=self.Entity) then
                
                    if( ent:GetClass()=="st_boiler" ) then
                        ent:Remove()
                        //Flag this check having an error concerning more than one core on the contraption
                        multicore_error = true                                  
                    end
                    if(!ent.Core) then
                        ent.Core = self.Entity
                    end
                    //This entity has another core. Destroy that one and make this one the entity's core
                    if( ent.Core:IsValid() and ent.Core!=self.Entity ) then     
                        ent.Core:Remove()
                        ent.Core = self.Entity
                        multicore_error = true
                    end
                    if( ent:GetClass()=="prop_physics" ) then
                        ent.Core = self.Entity
                        self.maxhp = self.maxhp + CalcHealth( ent )
                    end
                    if( ent:GetClass()=="st_weapon" ) then
                        self.maxhp = self.maxhp + ent.maxhp
                        checkweld = true
                        ent.Core = self.Entity
                    end
                    if( ent:GetClass()=="st_balloon" ) then
                        self.maxhp = self.maxhp + ent.maxhp
                        checkweld = true
                        ent.Core = self.Entity
                    end
                    if( ent:GetClass()=="st_fan" ) then
                        self.maxhp = self.maxhp + ent.maxhp
                        checkweld = true
                        ent.Core = self.Entity
                    end
                    if( ent:IsVehicle() and not self.seat:IsValid()) then
                        self.seat = ent
                        ent.Core = self.Entity
                    end
                    //If this entity should be welded and isnt, do so now, and flag it as an unwelded module error
                    if( checkweld == true) then     
                        table.insert( self.Mods , ent )
                        if( not WeldedTo(ent,self.Entity) and self.driverteam>-1) then
                            constraint.Weld(ent,self.Entity,0,0)
                            unweldedmods_error = true
                            print("st_boiler: welding "..tostring(ent).." to "..tostring(self.Entity))
                            self.Owner:ChatPrint("Welding "..ent:GetClass())
                        end
                    end
                    if(!table.HasValue(self.Ents, self.seat)) then
                        self.seat.Core = NULL
                        self.seat = NULL
                    end
                    
                end
            end
            
        if(multicore_error==true) then 
            self.Owner:ChatPrint("Cannot have more than one boiler on a contraption!") 
            multicore_error = false
        end
        if(unweldedmods_error==true and self.driverteam>-1) then 
            self.Owner:ChatPrint("All Sky Titans entities must be welded to the boiler!") 
            unweldedmods_error = false
        end
    
        //Return the HP to the propper percentage of the adjusted maxhp
        self.hp = math.floor(self.maxhp*hpratio)     
    
        self.checktimer = CurTime()+5
    end
    
    //Go through all attached modules and distribute steam pressure
    
    if( self.steamtimer < CurTime() ) then
        
        //increase boiler pressure
        self.steam = math.min( self.steam + self.buildup , self.steamcap )
        //Look through all the modules and see how much steam they are venting from the system
        local steamtotal = 0
        for n, mod in pairs( self.Mods ) do
            if(mod:IsValid()) then
                steamtotal = steamtotal + mod.steamvent
                //If there is a driver already and this module doesnt know it...
                if(self.driver:IsValid() and mod:GetNetworkedEntity("driver")==NULL) then
                    mod:SetNetworkedEntity("driver", self.driver)
                end
            else
                table.remove(self.Mods,n)
            end
        end
        //vent off the steam from the system
        self.steam = math.max( self.steam - steamtotal , 0 )
        
        self.pressure = math.Clamp(math.Round(self.steam)/self.steamcap,0,1)
        self:SetNetworkedString("Steam", "Steam: "..math.Round(self.steam).."/"..self.steamcap.."\nVent: "..math.ceil(steamtotal).."/"..math.ceil(self.buildup) )
        self:SetNetworkedString("Health", "Health: "..math.Round(self.hp).."/"..self.maxhp)
        
        self.steamtimer = CurTime()+0.2
    end
    
    //Check for the seat and driver, and get an aimpos from that.
    
    self.aimPos = Vector(0,0,0)
	if( self.seat:IsValid() and self.seat:IsVehicle() ) then
    
        local prevdriver = self.driver
		self.driver = self.seat:GetDriver()
        
        //If the driver has changed since the last run...
        if(self.driver==prevdriver) then /*comment*/ else
            
            //Update the core on who is driving
            if(self.driver:IsValid()) then
                self.lastdriver = self.driver
                self.driverteam = self.driver:Team()
            end
            
            //Update the mods clientside on who is driving
            for n, mod in pairs( self.Mods ) do
                if(mod:IsValid()) then
                    if(self.driver:IsValid()) then 
                        mod:SetNetworkedEntity("driver", self.driver)
                        print("Set new networked driver for "..tostring(mod))
                    else 
                        mod:SetNetworkedEntity("driver", NULL) 
                    end
                end
            end
        end
        
        //Only if the driver is valid, set it to the last driver
		if( self.driver:IsValid() and self.driver:IsPlayer() ) then
			self.aimPos = self.driver:GetShootPos()+self.driver:GetAimVector()*16000
		else
			self.aimPos = Vector(0,0,0)
		end
        
	end
    
    self.Entity:NextThink( CurTime() + 0.02 )
	return true
end

function ENT:OnRemove()
    self.Ents = constraint.GetAllConstrainedEntities( self.Entity )
	local ratio = self.hp/self.maxhp
	for n, ent in pairs( self.Ents ) do
		if(ent:IsValid()) then
			ent.maxhp = CalcHealth( ent )
			ent.hp = math.floor(ent.maxhp*ratio)
		end
	end
    
    RemoveRepay( self )
end

function ENT:OnRestore()
if WireLib then WireLib.Restored(self) end
end

function ENT:PreEntityCopy()
    if WireLib then
    local DupeInfo = WireLib.BuildDupeInfo(self)
        if DupeInfo then
            duplicator.StoreEntityModifier( self, "WireDupeInfo", DupeInfo )
        end
    end
end

function ENT:PostEntityPaste( Player, Ent, CreatedEntities )
    Ent.Owner = Player
    Ent_InitCost( self.Entity )
    if WireLib and (Ent.EntityMods) and (Ent.EntityMods.WireDupeInfo) then
        WireLib.ApplyDupeInfo(Player, Ent, Ent.EntityMods.WireDupeInfo, function(id) return CreatedEntities[id] end)
    end
end